package china.nanjing.mykingdom.serviceImpl;

import china.nanjing.mykingdom.common.KingdomConst;
import china.nanjing.mykingdom.object.Duchy;
import china.nanjing.mykingdom.object.Kingdom;
import china.nanjing.mykingdom.object.estates_general.Noble;
import china.nanjing.mykingdom.repository.DuchyRepository;
import china.nanjing.mykingdom.repository.KingdomRepository;
import china.nanjing.mykingdom.repository.NobleRepository;
import china.nanjing.mykingdom.service.MilitaryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 军事相关服务
 *
 * @author Chen Hongyu
 */
@Service(value = "militaryService")
@Slf4j
// 参考 https://www.baeldung.com/spring-cache-tutorial
// 参考 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/annotation/Cacheable.html
@CacheConfig(cacheNames = {"militaryService"}, keyGenerator = "keyGeneratorKingdom", cacheManager = "cacheManagerKingdom")
public class MilitaryServiceImpl implements MilitaryService {


    private KingdomRepository kingdomRepository;

    private DuchyRepository duchyRepository;

    private NobleRepository nobleRepository;

    @Autowired
    public MilitaryServiceImpl(KingdomRepository kingdomRepository, DuchyRepository duchyRepository, NobleRepository nobleRepository) {
        this.kingdomRepository = kingdomRepository;
        this.duchyRepository = duchyRepository;
        this.nobleRepository = nobleRepository;
    }


    /**
     * 计算公国军事总力量
     *
     * @param duchyId 公国id
     * @return 结果
     */
    @SuppressWarnings("unchecked")
    @Override
    @Cacheable
    public BigDecimal computeDuchyMilitaryStrength(Long duchyId) {
        // 此处可以使用my-cache模块提供的RedisTemplate, 或者使用Spring Cache自行为每个Maven项目配置.
        // 此处选择用Spring Cache, 虽然灵活性不如cache模块提供的RedisTemplate方式，但是方便本地开发
        // RedisTemplate<Object, Object> redisTemplate = RedisCacheProvider.getRedisTemplate();
        Optional<Duchy> duchy = duchyRepository.findById(duchyId);
        AtomicReference<BigDecimal> total = new AtomicReference<>(new BigDecimal(0));
        duchy.ifPresent(consumer -> {
            BigDecimal bd1 = total.get().add(consumer.getArmy());
            BigDecimal bd2 = bd1.add(consumer.getNavy());
            // 计算对应公国60岁以下的贵族的总兵力
            Specification nobleSpecification = (Specification) (root, criteriaQuery, criteriaBuilder) -> {
                List<Predicate> predicates = new ArrayList<>();
                // 注意这里是Noble的duchy字段，不是表里的duchy_id
                Predicate equal = criteriaBuilder.equal(root.get("duchy"), duchyId);
                Predicate le = criteriaBuilder.le(root.get("age"), KingdomConst.MAX_MILITARY_SERVICE_AGE);
                predicates.add(equal);
                predicates.add(le);
                criteriaQuery.orderBy(criteriaBuilder.asc(root.get("name")), criteriaBuilder.asc(root.get("age")));
                return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
            };
            List<Noble> nobles = nobleRepository.findAll(nobleSpecification);
            // 假设贵族每个人兵力=军事能力×100
            // https://www.baeldung.com/java-stream-reduce
            Integer sum = nobles.parallelStream().reduce(0, (x, y) -> x + y.getMartialSkill() * 100, Integer::sum);
            //公国总兵力=公国直辖陆军兵力+海军兵力+封臣贵族每个人的军事力量×100
            total.set(bd2.add(BigDecimal.valueOf(sum)));
        });
        return total.get();
    }

    /**
     * 计算公国年度军事花费
     *
     * @param duchyId 公国id
     * @return 结果
     */
    @Override
    public BigDecimal computeDuchyMilitaryExpenseAnnually(Long duchyId) {
        return null;
    }

    /**
     * 计算王国军事总力量
     *
     * @param kingdomId 王国id
     * @return 结果
     */
    @Override
    public BigDecimal computeKingdomMilitaryStrength(Long kingdomId) {
        return null;
    }

    /**
     * 计算王国军事总力量by 国名称
     *
     * @param kingdomName 王国名称
     * @return 结果
     */
    @Override
    public BigDecimal computeKingdomMilitaryStrengthByKingdomName(String kingdomName) {
        return null;
    }

    /**
     * 计算王国海军力量
     *
     * @param kingdomId 王国Id
     * @return 结果
     */
    @Override
    public BigDecimal computeKingdomNavyStrength(Long kingdomId) {
        return null;
    }

    /**
     * 计算王国陆军力量
     *
     * @param kingdomId 王国Id
     * @return 结果
     */
    @Override
    public BigDecimal computeKingdomArmyStrength(Long kingdomId) {
        return null;
    }

    /**
     * 按王国名称计算王国军事总力量
     *
     * @param kingdomName 王国名
     * @return 结果
     */
    @Override
    public BigDecimal computeKingdomMilitaryStrengthByName(String kingdomName) {
        // 这个方法是为测试WebFlux用的，暂时简化，就不计算公国和下属封臣的总军力了,也不缓存了
        AtomicReference<BigDecimal> total = new AtomicReference<>(new BigDecimal(0));
        Kingdom kingdom = kingdomRepository.findKingdomByKingdomName(kingdomName);
        return total.get().add(kingdom.getArmy()).add(kingdom.getNavy());
    }
}
